package cn.sweet.listener;

import cn.sweet.config.AppSettingState;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandEvent;
import com.intellij.openapi.command.CommandListener;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.TextEditorLocation;
import com.intellij.openapi.fileEditor.impl.CurrentEditorProvider;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.DocumentUtil;
import com.intellij.util.Producer;
import org.apache.commons.collections.CollectionUtils;
import org.jetbrains.annotations.NotNull;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 命令监听器
 *
 * @author wellhor Zhao
 * @version 1.0
 * @date 2021/12/8 7:03 PM
 **/
public class MyCommandListener implements CommandListener {

    private final static List<String> CHANGE_COMMAND = new ArrayList<>() {
        {
            add("Paste");
            add("Enter");
            add("Typing");
            add("Cut");
            add("Backspace");
        }
    };

    @Override
    public void commandFinished(@NotNull CommandEvent event) {
        if (event.getCommandName().startsWith("Undo")) {
            VirtualFile file = ApplicationManager.getApplication().getService(CurrentEditorProvider.class).getCurrentEditor().getFile();
            if (Objects.isNull(file)) {
                return;
            }
            FileState.remove(file.getPath());
        }
        System.out.println(event.getCommandName());
        if (CHANGE_COMMAND.contains(event.getCommandName())) {
            try {
                FileEditor editor = ApplicationManager.getApplication().getService(CurrentEditorProvider.class).getCurrentEditor();
                VirtualFile file = editor.getFile();
                // 判断文件是否已经修改过了
                if (Objects.isNull(file) || FileState.contain(file.getPath())) {
                    return;
                }
                if (fileNameMatch(file)) {
                    Document document = FileDocumentManager.getInstance().getCachedDocument(file);
                    if (Objects.nonNull(document)) {
                        AppSettingState appSettingState = AppSettingState.getInstance();
                        CharSequence charSequence = document.getCharsSequence();
                        List<ModifyEvent> modifyEvents = findModifyEvent(file, charSequence, document);
                        if (CollectionUtils.isNotEmpty(modifyEvents)) {
                            // 判断当前位置是都在注释范围内
                            FileEditorLocation fileEditorLocation = editor.getCurrentLocation();
                            if (!checkChangeIsInAnnotation(fileEditorLocation, modifyEvents)) {
                                doModify(modifyEvents, file);
                                // 如果不是每次都改 则去存储修改记录
                                if(!appSettingState.getIsAnyChange()) {
                                    FileState.add(file.getPath());
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                Notification notification = new Notification("Little-sweet-sweet", "Little sweet sweet plugin", "Little sweet sweet plugin report an exception： " + e.getMessage(), NotificationType.ERROR);
                Notifications.Bus.notify(notification);
            }
        }
    }

    /**
     * 判断修改位置是否在注释修改位置
     *
     * @param fileEditorLocation 修改位置
     * @param modifyEvents       判断当前修改是否为注释行
     * @return 是否在注释位置修改
     */
    private boolean checkChangeIsInAnnotation(FileEditorLocation fileEditorLocation, List<ModifyEvent> modifyEvents) {
        if (fileEditorLocation instanceof TextEditorLocation) {
            TextEditorLocation location = (TextEditorLocation) fileEditorLocation;
            LogicalPosition logicalPosition = location.getPosition();
            int line = logicalPosition.line;

            for (ModifyEvent event : modifyEvents) {
                if (event.getLine() == line) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean fileNameMatch(VirtualFile virtualFile) {
        AppSettingState appSettingState = AppSettingState.getInstance();
        String filePath = virtualFile.getPath();
        int pointIndex = filePath.lastIndexOf(".");
        if (pointIndex >= 0) {
            String fileSuffix = filePath.substring(pointIndex);
            if (appSettingState.getSuffixList().contains(fileSuffix) && !virtualFile.isDirectory()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查看文件内容 如果符合条件就修改
     *
     * @param virtualFile  被处理的文件
     * @param charSequence 文本
     * @param document     document
     * @return 需要修改的所有文件下标
     */
    private static List<ModifyEvent> findModifyEvent(VirtualFile virtualFile, CharSequence charSequence, Document document) {
        AppSettingState appSettingState = AppSettingState.getInstance();
        String lastEditTime = appSettingState.getLastEditTime();
        String lastEditors = appSettingState.getLastEditPerson();

        String userName = appSettingState.getUserName();
        String dateFormat = appSettingState.getTimeFormat();


        int lineLimit = appSettingState.getMaxCheckLineCount();
        List<ModifyEvent> modifyEvents = new ArrayList<>();

        for (int lineCount = 0; lineCount < document.getLineCount() && lineCount < lineLimit; lineCount++) {
            int start = document.getLineStartOffset(lineCount);
            int end = document.getLineEndOffset(lineCount);

            // 检查 最后修改时间注解
            int startIndex = check(charSequence, start, end, lastEditTime);
            if (startIndex >= 0) {
                ModifyEvent modifyEvent = modifyText(charSequence, startIndex, end, virtualFile, () -> lastEditTime + " " + new SimpleDateFormat(dateFormat).format(new Date()));
                modifyEvent.setLine(lineCount);
                modifyEvents.add(modifyEvent);
            }
            // 检查 最后修改人注解
            startIndex = check(charSequence, start, end, lastEditors);
            if (startIndex >= 0) {
                ModifyEvent modifyEvent = modifyText(charSequence, startIndex, end, virtualFile, () -> lastEditors + " " + userName);
                modifyEvent.setLine(lineCount);
                modifyEvents.add(modifyEvent);
            }
        }

        if (CollectionUtils.isNotEmpty(modifyEvents)) {
            modifyEvents.sort(Comparator.comparingInt(ModifyEvent::getBegin).reversed());
        }

        return modifyEvents;
    }

    private static void doModify(List<ModifyEvent> modifyEvents, VirtualFile virtualFile) {
        if (CollectionUtils.isNotEmpty(modifyEvents)) {
            Document document = FileDocumentManager.getInstance().getCachedDocument(virtualFile);
            if (Objects.nonNull(document)) {
                DocumentUtil.writeInRunUndoTransparentAction(() -> {
                    modifyEvents.forEach(modifyEvent -> {
                        if (modifyEvent.getEnd() > modifyEvent.getBegin()) {
                            document.deleteString(modifyEvent.getBegin(), modifyEvent.getEnd());
                        }
                        document.insertString(modifyEvent.getBegin(), modifyEvent.getTargetTextProducer().produce());
                    });
                });
            }
        }
    }

    private static ModifyEvent modifyText(CharSequence charSequence, int start, int end, VirtualFile virtualFile, Producer<String> targetTextProducer) {
        ModifyEvent modifyEvent = new ModifyEvent();
        modifyEvent.setCharSequence(charSequence);
        modifyEvent.setBegin(start);
        modifyEvent.setEnd(end);
        modifyEvent.setTargetTextProducer(targetTextProducer);
        modifyEvent.setVirtualFile(virtualFile);
        return modifyEvent;
    }

    /**
     * 检查是否命中了字符
     *
     * @param charSequence 原数组
     * @param start        开始下标
     * @param end        结束下标
     * @param targetText   目标匹配字符序列
     * @return 是否匹配
     */
    private static int check(CharSequence charSequence, int start, int end, String targetText) {
        char firstChar = targetText.charAt(0);
        for (int i = start; i < end; i++) {
            if (i + targetText.length() > charSequence.length()) {
                return -1;
            }
            if (charSequence.charAt(i) == firstChar) {
                boolean flag = true;
                for (int j = 1; j < targetText.length(); j++) {
                    if (charSequence.charAt(i + j) != targetText.charAt(j)) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    return i;
                }
            }
        }
        return -1;
    }

}
